package cs6238.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.List;

import cs6238.common.Communication;
import cs6238.common.FileInfo;
import cs6238.common.message.command.Command;
import cs6238.common.message.command.DelegateCommand;
import cs6238.common.message.command.GetFileCommand;
import cs6238.common.message.command.ListHostsCommand;
import cs6238.common.message.command.LsCommand;
import cs6238.common.message.command.PutFileCommand;
import cs6238.common.message.response.ListHostsResponse;
import cs6238.common.message.response.LsResponse;
import cs6238.common.message.response.StandardResponse;

/**
 * This will run as a separate thread for each client, and handle communication and 
 * access to server resources
 *
 */
public class ClientHandlerCommunication extends Communication  implements Runnable{

	private String _clientHostname;
	private FileAndDelegateEntryTable _table;
	
	public ClientHandlerCommunication(Socket socket, FileAndDelegateEntryTable table, String hostname) throws IOException{
		super(socket);
		_table = table;
		_clientHostname = hostname;
	}
	
	protected ClientHandlerCommunication(InputStream input, OutputStream output, FileAndDelegateEntryTable table) throws IOException{
		super(input, output);
		_table = table;
	}

	
	@Override
	public void run() {
		try{
			while(true){
				Command clientCommand = this.receiveCommand();
				
				if(clientCommand != null){
					executeCommand(clientCommand);
				}
				else{
					System.err.println("Invalid command: " + clientCommand);
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	
	
	public void executeCommand(Command command) throws Exception{
		if(command instanceof LsCommand){
			executeCommand((LsCommand)command);
		}
		else if(command instanceof GetFileCommand){
			executeCommand((GetFileCommand)command);
		}
		
		else if(command instanceof PutFileCommand){
			executeCommand((PutFileCommand)command);
		}
		else if(command instanceof DelegateCommand){
			executeCommand((DelegateCommand)command);
		}
		else if(command instanceof ListHostsCommand){
			executeCommand((ListHostsCommand)command);
		}
		else{
			System.err.println("ERRRR..how did I get here?");
		}
	}

	public void executeCommand(GetFileCommand command) throws Exception {
		if(_table.isAllowedToGet(_clientHostname, command.getFileId())){
			this.sendResponse(new StandardResponse(true));
			FileEntry entry = _table.getFileEntry(command.getFileId());
			byte[] decryptedFile = FileManager.getFile(entry);
			this.sendFileAsBytes(decryptedFile);
		}
		else{
			this.sendResponse(new StandardResponse(false));
		}
	}

	public void executeCommand(PutFileCommand command) throws IOException {
		this.sendResponse(new StandardResponse(true));
		
		byte[] fileContents = this.receiveFile();
		try {
			FileManager.putFile(fileContents, command.getClientFilename(), _clientHostname, _table);
		} catch (Exception e) {
			this.sendResponse(new StandardResponse(false));
		}
		this.sendResponse(new StandardResponse(true));
		
	}

	public void executeCommand(DelegateCommand command) throws IOException {
		if(DelegationVerifier.isDelegationValid(command.getDelegateEntry())){
			_table.addDelegateEntry(command.getDelegateEntry());
			this.sendResponse(new StandardResponse(true));
		}
		else{
			this.sendResponse(new StandardResponse(false));
		}
	}

	public void executeCommand(LsCommand command) throws IOException {
		List<FileInfo> fileInfos = _table.getFilesAvailableToHost(_clientHostname);
		LsResponse response = new LsResponse();
		response.setFileInfoList(fileInfos);
		this.sendResponse(response);
	}
	
	public void executeCommand(ListHostsCommand command) throws IOException{
		ListHostsResponse response = new ListHostsResponse();
		response.setHosts(ClientCertManager.getHostnames());
		this.sendResponse(response);
	}
}
